﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using DistanceLearning.Filters;
using DistanceLearning.Models;
using WebMatrix.WebData;

namespace DistanceLearning.Controllers
{
    [Authorize(Roles = JournalStatic.SchoolAdminRole)]
    [InitializeSimpleMembership]
    public class AdministrationController : JournalBaseController
    {
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult GetTeacherListBySubject(string subjectName)
        {
            var teachers = JournalContext.TeacherSubjects
                .Where(ts => ts.SubjectID == subjectName)
                .Select(ts => new TeacherWrapper(ts.Teacher));
            return Json(teachers);
        }

        private UserProfile _currentUser;
        private UserProfile CurrentUser
        {
            get
            {
                if (_currentUser == null)
                {
                    var userName = User.Identity.Name;
                    _currentUser = UsersContext.UserProfiles.FirstOrDefault(x => x.UserName == userName);
                }
                return _currentUser;
            }
        }

        //[Authorize(Roles = "Admin")]
        [Authorize]
        public ActionResult Administration()
        {
            var journals = JournalContext.Journals;
            return View(journals);
        }

        [HttpPost]
        public void AddNewNode(string nodeName, string parentNodeType)
        {
            Journal journal;
            switch (parentNodeType)
            {
                case JsTreeNodeType.Subjects:
                    var subject = new Subject {SubjectName = nodeName};
                    JournalContext.Subjects.InsertOnSubmit(subject);
                    JournalContext.SubmitChanges();
                    break;
                case JsTreeNodeType.PupilClass:
                    journal = new Journal {JournalName = nodeName, Year = DateTime.Now.Year};
                    JournalContext.Journals.InsertOnSubmit(journal);
                    JournalContext.SubmitChanges();
                    break;
                //case JsTreeNodeType.Pupil:
                //    journal = JournalContext.Journals.First(j => j.JournalName == parentId);
                //    journal.Pupils.Add(new Pupil(){JournalID = journal.JournalID});
                //    break;
                case JsTreeNodeType.Default:
                    break;

            }
        }

        [HttpPost]
        public void MoveNode(string userDetailsId, string toNodeType, string toNodeName)
        {
            int userId = int.Parse(userDetailsId); 
            var userToMove = JournalContext.UserDetails.FirstOrDefault(ud => ud.GlobalProfileID == userId);
            var userProfile = UsersContext.UserProfiles.FirstOrDefault(u => u.UserId == userId);
            if (userToMove == null) return;

            switch (toNodeType)
            {
                case JsTreeNodeType.PupilClass:
                    var newPupil = new Pupil { PupilID = userId, JournalID = int.Parse(toNodeName) };
                    JournalContext.Pupils.InsertOnSubmit(newPupil);
                    JournalContext.SubmitChanges();
                    Roles.AddUserToRole(userProfile.UserName, JournalStatic.Pupil);
                    break;
                
                case JsTreeNodeType.Teachers:
                    var newTeacher = new Teacher {TeacherID = userId};
                    JournalContext.Teachers.InsertOnSubmit(newTeacher);
                    JournalContext.SubmitChanges();
                    Roles.AddUserToRole(userProfile.UserName, JournalStatic.Teacher);
                    break;

                case JsTreeNodeType.Parents:
                    var newParent = new Parent {ParentID = userId};
                    JournalContext.Parents.InsertOnSubmit(newParent);
                    JournalContext.SubmitChanges();
                    Roles.AddUserToRole(userProfile.UserName, JournalStatic.Parent);
                    break;
            }
        }

        [HttpPost]
        public void RemoveNode(string nodeType, string nodeName)
        {
            switch (nodeType)
            {
                case JsTreeNodeType.PupilClass:
                    var pclass = JournalContext.Journals.First(x => x.JournalID == int.Parse(nodeName));
                    JournalContext.Journals.DeleteOnSubmit(pclass);
                    JournalContext.SubmitChanges();
                    break;
                case JsTreeNodeType.Pupil:
                    var pupil = JournalContext.Pupils.First(p => p.PupilID == int.Parse(nodeName));
                    JournalContext.Pupils.DeleteOnSubmit(pupil);
                    JournalContext.SubmitChanges();
                    break;
            }
        }

        [HttpPost]
        public void RenameNode(string nodeType, string nodeId, string newNodeName)
        {
            switch (nodeType)
            {
                case JsTreeNodeType.PupilClass:
                    //var oldClass = JournalContext.Classes.First(c => c.ClassName == oldNodeName);
                    //JournalContext.Classes.DeleteOnSubmit(oldClass);
                    //JournalContext.SubmitChanges();
                    //var oldClass = JournalContext.Classes.First(c => c.ClassName == oldNodeName);
                    //JournalContext.Classes.DeleteOnSubmit(oldClass);
                    //oldClass.ClassName = newNodeName;
                    //JournalContext.SubmitChanges();

                    //var newJournal = new Journal {Class = new Class {ClassName = newNodeName}};
                    //JournalContext.Journals.InsertOnSubmit(newJournal);
                    //JournalContext.SubmitChanges();
                    //var oldClass = JournalContext.Classes.First(c => c.ClassName == oldNodeName);
                    //oldClass.ClassName = newNodeName;

                    var journal = JournalContext.Journals.First(j => j.JournalID == int.Parse(nodeId));
                    journal.JournalName = newNodeName;
                    JournalContext.SubmitChanges();
                    break;
                case JsTreeNodeType.Pupil:
                    break;
            }
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult GetJournalData()
        {
            var tree = new List<JsTreeModel>();
            foreach (var journal in JournalContext.Journals)
            {
                var treeNode = new JsTreeModel();
                treeNode.data = journal.JournalName;
                treeNode.children = new List<JsTreeModel>();
                treeNode.attr = new JsTreeAttribute {rel = JsTreeNodeType.PupilClass, id = journal.JournalID.ToString()};
                foreach (var pupil in JournalContext.Pupils.Where(p => p.JournalID == journal.JournalID).Select(x => x))
                {
                    var pupilNode = new JsTreeModel();
                    pupilNode.data = string.Format("{0} {1}", pupil.UserDetails.FirstName, pupil.UserDetails.LastName);
                    pupilNode.attr = new JsTreeAttribute
                                         {rel = JsTreeNodeType.Pupil, id = pupil.PupilID.ToString()};
                    treeNode.children.Add(pupilNode);
                }
                var timetableNode = new JsTreeModel();
                timetableNode.data = "Расписание занятий";
                timetableNode.children = new List<JsTreeModel>();
                timetableNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Timetable, id = JsTreeNodeType.Timetable };
                foreach (var dayOfWeek in DateHelper.GetDaysOfWeek())
                {
                    var dayOfWeekNode = new JsTreeModel();
                    dayOfWeekNode.data = DateHelper.GetRussianValue(dayOfWeek.Value);
                    dayOfWeekNode.attr = new JsTreeAttribute {rel = JsTreeNodeType.TimetableDayOfWeek, id = string.Format("{0}//{1}", dayOfWeek.Key, journal.JournalID)};
                    timetableNode.children.Add(dayOfWeekNode);
                }
                treeNode.children.Add(timetableNode);
                tree.Add(treeNode);
            }

            var year = new JsTreeModel();
            year.data = "Учебный год";
            year.children = new List<JsTreeModel>();
            year.attr = new JsTreeAttribute { rel = JsTreeNodeType.AcademicYear, id = JsTreeNodeType.AcademicYear };

            var quartersNode = new JsTreeModel();
            quartersNode.data = "Четверти";
            quartersNode.attr = new JsTreeAttribute {rel = JsTreeNodeType.Quarters, id = JsTreeNodeType.Quarters};
            year.children.Add(quartersNode);

            var scheduleNode = new JsTreeModel();
            scheduleNode.data = "Расписание звонков";
            scheduleNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Schedule, id = JsTreeNodeType.Schedule };
            year.children.Add(scheduleNode);
            tree.Add(year);

            var teachersTreeNode = new JsTreeModel();
            teachersTreeNode.data = "Преподаватели";
            teachersTreeNode.children = new List<JsTreeModel>();
            teachersTreeNode.attr = new JsTreeAttribute {rel = JsTreeNodeType.Teachers, id = JsTreeNodeType.Teachers};
            foreach(var teacher in JournalContext.Teachers)
            {
                var teacherNode = new JsTreeModel();
                teacherNode.data = string.Format("{0} {1} {2}", teacher.UserDetails.LastName, teacher.UserDetails.FirstName, teacher.UserDetails.PatronymicName);
                teacherNode.attr = new JsTreeAttribute
                                       {rel = JsTreeNodeType.Teacher, id = teacher.TeacherID.ToString()};
                teachersTreeNode.children.Add(teacherNode);
            }
            tree.Add(teachersTreeNode);

            var parentsTreeNode = new JsTreeModel();
            parentsTreeNode.data = "Родители";
            parentsTreeNode.children = new List<JsTreeModel>();
            parentsTreeNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Parents, id = JsTreeNodeType.Teachers };
            foreach (var parent in JournalContext.Parents)
            {
                var parentNode = new JsTreeModel();
                parentNode.data = string.Format("{0} {1} {2}", parent.UserDetails.LastName, parent.UserDetails.FirstName, parent.UserDetails.PatronymicName);
                parentNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Parent, id = parent.ParentID.ToString() };
                parentsTreeNode.children.Add(parentNode);
            }
            tree.Add(parentsTreeNode);

            var subjectsTreeNode = new JsTreeModel();
            subjectsTreeNode.data = "Дисциплины";
            subjectsTreeNode.children = new List<JsTreeModel>();
            subjectsTreeNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Subjects, id = JsTreeNodeType.Subjects };
            foreach (var subject in JournalContext.Subjects)
            {
                var subjectNode = new JsTreeModel();
                subjectNode.data = subject.SubjectName;
                subjectNode.attr = new JsTreeAttribute { rel = JsTreeNodeType.Subject, id = subject.SubjectName };
                subjectsTreeNode.children.Add(subjectNode);
            }
            tree.Add(subjectsTreeNode);

            // users are not teachers and students
            var undefinedUsers = 
                JournalContext.UserDetails.Where(ud => 
                    JournalContext.Pupils.All(x => x.PupilID != ud.GlobalProfileID) &&
                    JournalContext.Teachers.All(x => x.TeacherID != ud.GlobalProfileID)).ToList();

            if (undefinedUsers.Count > 0)
            {
                var undefClassNode = new JsTreeModel();
                undefClassNode.data = "Неопределенные пользователи";
                undefClassNode.children = new List<JsTreeModel>();
                undefClassNode.attr = new JsTreeAttribute {rel = JsTreeNodeType.UsersContainer, id = "undef_class"};
                foreach (var user in undefinedUsers)
                {
                    var undefinedNode = new JsTreeModel();
                    undefinedNode.data = string.Format("{0} {1} {2}", user.LastName, user.FirstName, user.PatronymicName);
                    undefinedNode.attr = new JsTreeAttribute
                                             {
                                                 rel = JsTreeNodeType.Default,
                                                 id = user.GlobalProfileID.ToString()
                                             };
                    undefClassNode.children.Add(undefinedNode);
                }
                tree.Add(undefClassNode);
            }

            return Json(tree);
        }

        private IQueryable<Quarter> GetOrCreateQuareters()
        {
            for (int i = 0; i < 4; ++i)
            {
                if (!JournalContext.Quarters.Any(q => q.QuarterID == i))
                {
                    var stubQuarter = new Quarter {StartDate = DateTime.Now, EndDate = DateTime.Now, QuarterID = i};
                    JournalContext.Quarters.InsertOnSubmit(stubQuarter);
                }
            }
            JournalContext.SubmitChanges();
            return JournalContext.Quarters;
        }
        private IQueryable<LessonTime> GetOrCreateSchedule()
        {
            for (int i = 0; i < 8; ++i)
            {
                if (!JournalContext.LessonTimes.Any(q => q.Order == i))
                {
                    var stubQuarter = new LessonTime { Order = i, StartTime = DateTime.Now };
                    JournalContext.LessonTimes.InsertOnSubmit(stubQuarter);
                }
            }
            JournalContext.SubmitChanges();
            return JournalContext.LessonTimes;
        }

        [HttpPost]
        public ActionResult SaveQuarters(IEnumerable<Quarter> quarters)
        {
            foreach (var item in quarters)
            {
                var existing = JournalContext.Quarters.FirstOrDefault(q => q.QuarterID == item.QuarterID);
                existing.StartDate = item.StartDate;
                existing.EndDate = item.EndDate;
            }

            JournalContext.SubmitChanges();
            return null;
        }

        [HttpPost]
        public ActionResult SaveSchedule(IEnumerable<LessonTime> times)
        {
            foreach (var item in times)
            {
                var existing = JournalContext.LessonTimes.FirstOrDefault(q => q.Order == item.Order);
                existing.StartTime = item.StartTime;
            }

            JournalContext.SubmitChanges();
            return null;
        }

        public PartialViewResult NodeDetails(string id, string nodeType)
        {
            //Thread.Sleep(1000);
            ViewBag.Subjects = JournalContext.Subjects.ToArray();
            ViewBag.Teachers = JournalContext.Teachers.ToArray();
            ViewBag.Parents = JournalContext.Parents.ToArray();
            int userId;
            int.TryParse(id, out userId);
            switch (nodeType)
            {
                case JsTreeNodeType.PupilClass:
                    var classEntity = JournalContext.Journals.First(c => c.JournalID == int.Parse(id));
                    return PartialView(@"_ClassDetails", classEntity);
                case JsTreeNodeType.Pupil:
                    var userDetails = JournalContext.Pupils.First(ud => ud.PupilID == userId);
                    return PartialView(@"_PupilDetails", userDetails);
                case JsTreeNodeType.Teacher:
                    var teacher = JournalContext.Teachers.First(t => t.TeacherID == userId);
                    return PartialView(@"_TeacherDetails", teacher);
                case JsTreeNodeType.TimetableDayOfWeek:
                    var ids = id.Split(new []{'/'}, StringSplitOptions.RemoveEmptyEntries);
                    var dayOfWeek = int.Parse(ids[0]);
                    var journalId = int.Parse(ids[1]);

                    var lessonList = new List<JournalLesson>();
                    for (int order = 0; order < 8; ++order)
                    {
                        var lesson = JournalContext.JournalLessons.FirstOrDefault(l => l.Order == order && l.DayOfWeek == dayOfWeek && l.JournalID == journalId);
                        if (lesson == null)
                        {
                            lesson = new JournalLesson();
                            lesson.Order = order;
                            lesson.DayOfWeek = dayOfWeek;
                            lesson.JournalID = journalId;
                        }
                        lessonList.Add(lesson);
                    }
                    return PartialView(@"_CreateTimetable", lessonList);
                case "register_new_user":
                    var registerModel = new RegisterModel();
                    var password = GeneratePassword();
                    registerModel.Password = password;
                    registerModel.ConfirmPassword = password;
                    registerModel.UserName = GenerateUserName();
                    registerModel.DateOfBirth = DateTime.Now;
                    registerModel.SendEmail = true;
                    return PartialView(@"_Register", registerModel);
                case JsTreeNodeType.Quarters:
                    return PartialView("_Quarters", GetOrCreateQuareters().ToArray());
                case JsTreeNodeType.Schedule:
                    return PartialView("_Schedule", GetOrCreateSchedule().ToArray());
            }
            return new PartialViewResult();
        }

        [HttpPost]
        public ViewResult CreateTimetable(IEnumerable<JournalLesson> concreteLesson)
        {
            foreach(var lesson in concreteLesson)
            {
                bool contains = JournalContext.JournalLessons.Contains(lesson);

                if (lesson.TeacherID != 0 && !string.IsNullOrEmpty(lesson.SubjectID))
                {
                    if (contains)
                    {
                        var original = JournalContext.JournalLessons.First(l => l == lesson);
                        original.TeacherID = lesson.TeacherID;
                        original.SubjectID = lesson.SubjectID;
                    }
                    else
                        JournalContext.JournalLessons.InsertOnSubmit(lesson);
                }
                else if (contains)
                    JournalContext.JournalLessons.DeleteOnSubmit(lesson);
            }
            
            JournalContext.SubmitChanges();

            return null;
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public PartialViewResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {
                    using (var db = new UsersContext())
                    {
                        if (!WebSecurity.UserExists(model.UserName))
                        {
                            var userProfile = new UserProfile();
                            userProfile.SchoolId = CurrentUser.School.SchoolId;
                            userProfile.UserName = model.UserName;
                            db.UserProfiles.InsertOnSubmit(userProfile);
                            db.SubmitChanges();

                            WebSecurity.CreateAccount(model.UserName, model.Password);
                            var newUserId = WebSecurity.GetUserId(model.UserName);

                            var userDetails = new UserDetails();
                            userDetails.GlobalProfileID = newUserId;
                            userDetails.FirstName = model.FirstName;
                            userDetails.LastName = model.LastName;
                            userDetails.PatronymicName = model.PatronymicName;
                            userDetails.DateOfBirth = model.DateOfBirth;
                            userDetails.Email = model.Email;
                            JournalContext.UserDetails.InsertOnSubmit(userDetails);
                            JournalContext.SubmitChanges();

                            if (model.SendEmail)
                            {
                                var body = string.Format("Уважаемый {0} {1} {2}!{3}Вы успешно зарегистрированы на портале \"Диалог\".{3} Логин: {4}{3} Пароль: {5}",
                                        userDetails.LastName, userDetails.FirstName, userDetails.PatronymicName,
                                        Environment.NewLine, model.UserName, model.Password);
                                EmailService.EmailSender.SendText(userDetails.Email, "Регистрация на портале \"Диалог\"", body);
                            }
                        }
                    }

                    return null;
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", AccountController.ErrorCodeToString(e.StatusCode));
                }
            }
            // If we got this far, something failed, redisplay form
            return PartialView("_Register", model);
        }

        [HttpPost]
        public ActionResult TeacherSubjectsDetailsAndAdd(int teacherId, string subjectName)
        {
            var teacher = JournalContext.Teachers.First(t => t.TeacherID == teacherId);
            JournalContext.TeacherSubjects.InsertOnSubmit(new TeacherSubject{SubjectID = subjectName, TeacherID = teacherId});
            JournalContext.SubmitChanges();

            var subjects = teacher.TeacherSubjects.Select(ts => ts.Subject).ToList();
            return PartialView("_TeacherSubjects", subjects);
        }

        [HttpPost]
        public ActionResult ParentChildDetailsAndAdd(int pupilId, int parentId)
        {
            var pupil = JournalContext.Pupils.First(p => p.PupilID == pupilId);
            JournalContext.ParentChilds.InsertOnSubmit(new ParentChild {ChildID = pupilId, ParentID = parentId});
            JournalContext.SubmitChanges();

            var parents = pupil.ParentChilds.Select(pc => pc.Parent).ToList();
            return PartialView("_ParentList", parents);
        }

        private string GeneratePassword()
        {
            return Membership.GeneratePassword(7, 2);
        }

        private string GenerateUserName()
        {
            return DateTime.Now.GetHashCode().ToString(CultureInfo.InvariantCulture);
        }

        [HttpPost]
        public Action CreateLessons()
        {
            var startDate = JournalContext.Quarters.First(q => q.QuarterID == 0).StartDate;
            var endDate = JournalContext.Quarters.First(q => q.QuarterID == 3).EndDate;

            var dates = new Dictionary<DayOfWeek, List<DateTime>>();
            
            var curDate = startDate;
            while(curDate <= endDate)
            {
                List<DateTime> values;
                if(!dates.TryGetValue(curDate.DayOfWeek, out values))
                {
                    values = new List<DateTime>();
                    dates.Add(curDate.DayOfWeek, values);
                }
                values.Add(curDate);
                curDate = curDate.AddDays(1);
            }

            var lessonTimes = JournalContext.LessonTimes.ToArray();
            if (!JournalContext.SchoolConfigurations.Any(sc => sc.Key == ConfigurationTableHelper.Timetable))
            {
                foreach (var lesson in JournalContext.JournalLessons)
                {
                    List<DateTime> values;
                    if (dates.TryGetValue(DateHelper.GetDayOfWeek(lesson.DayOfWeek), out values))
                    {
                        foreach (var date in values)
                        {
                            var starTime = lessonTimes[lesson.Order].StartTime;
                            DateTime lessonStartTime = date + new TimeSpan(starTime.Hour, starTime.Minute, starTime.Second); // время начала урока
                            var concreteLesson = JournalContext.ConcreteLessons.FirstOrDefault(c =>
                                c.LessonOrder == lesson.Order &&
                                c.DayOfWeek == lesson.DayOfWeek &&
                                c.Date == lessonStartTime &&
                                c.JournalID == lesson.JournalID);
                            if (concreteLesson == null)
                            {
                                concreteLesson = new ConcreteLesson();
                                concreteLesson.LessonOrder = lesson.Order;
                                concreteLesson.JournalID = lesson.JournalID;
                                concreteLesson.DayOfWeek = lesson.DayOfWeek;
                                concreteLesson.Date = lessonStartTime;
                                JournalContext.ConcreteLessons.InsertOnSubmit(concreteLesson);
                            }

                            foreach (var pupil in lesson.Journal.Pupils)
                            {
                                if (!JournalContext.ConcreteLessonPupilDetails.Any(c =>
                                    c.ConcreteLesson.ID == concreteLesson.ID &&
                                    c.Pupil.PupilID == pupil.PupilID)) // TODO: check this
                                {
                                    var concreteLessonPupilDetails = new ConcreteLessonPupilDetails();
                                    concreteLessonPupilDetails.ConcreteLesson = concreteLesson;
                                    concreteLessonPupilDetails.PupilID = pupil.PupilID;
                                    JournalContext.ConcreteLessonPupilDetails.InsertOnSubmit(concreteLessonPupilDetails);
                                }
                            }
                        }
                    }
                }
            }
            try
            {
                //JournalContext.SchoolConfigurations.InsertOnSubmit(new SchoolConfiguration
                //{
                //    Key = ConfigurationTableHelper.Timetable,
                //    Value = "Created"
                //});
                JournalContext.SubmitChanges();
            }
            catch (Exception e)
            {
            }
            return null;
        }
    }
}
